﻿using Digitalmes.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Digitalmes
{
    /// <summary>
    /// 错误代码。
    /// </summary>
    /// <remarks>
    /// <para></para>
    /// <para>E0000~E0099 设备交互动作代码</para>
    /// <para>E1000~E1999 设备交互错误代码</para>
    /// <para>E4000~E4999 系统业务错误代码</para>
    /// </remarks>
    [Description("业务错误代码")]
    public enum ErrorCode
    {
        #region 设备交互动作代码

        /// <summary>
        /// 【回执给设备-进站/出站】OK
        /// </summary>
        [Description("OK")]
        E0000 = 0,

        /// <summary>
        /// 【回执给设备-进站/出站】 OK
        /// </summary>
        [Description("OK")]
        E0001 = 1,

        /// <summary>
        /// 【回执给设备-进站/出站】系统异常
        /// </summary>
        [Description("系统异常")]
        E0002 = 2,

        /// <summary>
        /// 【回执给设备】强制 OK
        /// </summary>
        [Description("强制 OK")]
        E0003 = 3,

        /// <summary>
        /// 【回执给设备】强制 NG
        /// </summary>
        [Description("强制 NG")]
        E0004 = 4,

        /// <summary>
        /// MES 反馈 PLC 处理 OK。
        /// </summary>
        [Description("允许作业")]
        E0010 = 10,

        /// <summary>
        /// 【进站】放行，该产品在此工位无需作业，流入后一工序。
        /// </summary>
        [Description("放行")]
        E0020 = 20,

        #endregion

        #region 设备交互异常代码

        #region 上线请求

        /// <summary>
        /// 【产品上线请求】产品上线请求异常。
        /// </summary>
        [Description("产品上线请求异常")]
        E1000 = 1000,

        /// <summary>
        /// 【产品上线请求】PLC 上线请求数据不完整，SN 为空。
        /// </summary>
        [Description("PLC 上线请求数据不完整，SN 为空")]
        E1001 = 1001,

        /// <summary>
        /// 【产品上线请求】PLC 上线请求数据不完整，配方号为空。
        /// </summary>
        [Description("PLC 上线请求数据不完整，配方号为空")]
        E1002 = 1002,

        /// <summary>
        /// 【产品上线请求】产品上线请求异常，产品重复上线。
        /// </summary>
        [Description("产品上线请求异常，产品重复上线")]
        E1003 = 1003,

        #endregion

        #region 进站

        /// <summary>
        /// 【进站】进站请求异常。
        /// </summary>
        [Description("进站请求异常")]
        E1100 = 1100,

        /// <summary>
        /// 【进站】PLC 进站请求数据不完整，SN 为空。
        /// </summary>
        [Description("PLC 进站请求数据不完整，SN 为空")]
        E1101 = 1101,

        /// <summary>
        /// 【进站】PLC 进站请求数据不完整，配方号为空。
        /// </summary>
        [Description("PLC 进站请求数据不完整，配方号为空")]
        E1102 = 1102,

        /// <summary>
        /// 【进站】进站请求异常，没有找到指定的工位信息。
        /// </summary>
        [Description("进站请求异常，没有找到指定的工位信息")]
        E1103 = 1103,

        /// <summary>
        /// 【进站】进站请求异常，通过配方号没有找到指定的产品。
        /// </summary>
        [Description("进站请求异常，通过配方号没有找到指定的产品")]
        E1104 = 1104,

        /// <summary>
        /// 【进站】进站请求异常，没有可执行的工单。
        /// </summary>
        [Description("进站请求异常，没有可执行的工单")]
        E1105 = 1105,

        /// <summary>
        /// 【进站】进站请求异常，产品条码规则不匹配。
        /// </summary>
        [Description("进站请求异常，产品条码规则不匹配")]
        E1106 = 1106,

        /// <summary>
        /// 【进站】进站请求异常，通过配方号没有找到对应的工艺路线。
        /// </summary>
        [Description("进站请求异常，通过配方号没有找到对应的工艺路线")]
        E1107 = 1107,

        /// <summary>
        /// 【进站】进站请求异常，产品首次必须从首站上线。
        /// </summary>
        [Description("进站请求异常，产品首次必须从首站上线")]
        E1108 = 1108,

        /// <summary>
        /// 【进站】进站请求异常，当前工位没有找到对应的工序信息。
        /// </summary>
        [Description("进站请求异常，当前工位没有找到对应的工序信息")]
        E1109 = 1109,

        /// <summary>
        /// 【进站】进站请求异常，产品已完工下线，不能再进站。
        /// </summary>
        [Description("进站请求异常，产品已完工下线，不能再进站")]
        E1110 = 1110,

        /// <summary>
        /// 【进站】进站请求异常，当前工位不是要返工上件的工位。
        /// </summary>
        [Description("进站请求异常，当前工位不是要返工上件的工位")]
        E1111 = 1111,

        /// <summary>
        /// 【进站】进站请求异常，当前工序重复进站。
        /// </summary>
        [Description("进站请求异常，当前工序重复进站")]
        E1112 = 1112,

        /// <summary>
        /// 【进站】进站请求异常，存在跳序作业。
        /// </summary>
        [Description("进站请求异常，存在跳序作业")]
        E1113 = 1113,

        /// <summary>
        /// 【进站】进站请求异常，上一工位过站NG。
        /// </summary>
        [Description("进站请求异常，上一工位过站NG")]
        E1114 = 1114,

        /// <summary>
        /// 【进站】进站请求异常，没有找到相应的批次。
        /// </summary>
        [Description("进站请求异常，没有找到相应的批次")]
        E1121 = 1121,

        /// <summary>
        /// 【进站】进站请求异常，当前批次使用数量已达上限。
        /// </summary>
        [Description("进站请求异常，当前批次使用数量已达上限")]
        E1122 = 1122,

        #endregion

        #region 出站

        /// <summary>
        /// 【出站】产品出站异常。
        /// </summary>
        [Description("出站请求异常")]
        E1200 = 1200,

        /// <summary>
        /// 【出站】PLC 出站请求数据不完整，SN 为空。
        /// </summary>
        [Description("PLC 出站请求数据不完整，SN 为空")]
        E1201 = 1201,

        /// <summary>
        /// 【出站】PLC 出站请求数据不完整，配方号为空。
        /// </summary>
        [Description("PLC 出站请求数据不完整，配方号为空")]
        E1202 = 1202,

        /// <summary>
        /// 【出站】PLC 出站请求数据不完整，过站结果没有设定。
        /// </summary>
        [Description("PLC 出站请求数据不完整，过站结果没有设定")]
        E1203 = 1203,

        /// <summary>
        /// 【出站】产品出站异常，SN 不在当前工位。
        /// </summary>
        [Description("产品出站异常，SN 不在当前工位")]
        E1204 = 1204,

        /// <summary>
        /// 【出站】产品出站异常，SN 在当前工位已出站，不能重复出站。
        /// </summary>
        [Description("产品出站异常，SN 在当前工位已出站，不能重复出站")]
        E1205 = 1205,

        /// <summary>
        /// 【出站】产品出站异常，SN 在当前工位没有进站请求。
        /// </summary>
        [Description("产品出站异常，SN 在当前工位没有进站请求")]
        E1206 = 1206,

        /// <summary>
        /// 【出站】产品出站异常，已上的关键物料与 BOM 物料清单不匹配，存在关键物料未扫码。
        /// </summary>
        [Description("产品出站异常，已上的关键物料与 BOM 物料清单不匹配，存在关键物料未扫码")]
        E1207 = 1207,

        #endregion

        #region 关键物料扫码上料

        /// <summary>
        /// 【关键物料扫码上料】关键物料扫码上料异常。
        /// </summary>
        [Description("关键物料扫码上料请求异常")]
        E1300 = 1300,

        /// <summary>
        /// 【关键物料扫码上料】PLC 扫码请求数据不完整，Barcode 为空。
        /// </summary>
        [Description("PLC 扫码请求数据不完整，Barcode 为空")]
        E1301 = 1301,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，当前工位没有找到 SN。
        /// </summary>
        [Description("扫码上料请求异常，当前工位没有找到 SN")]
        E1302 = 1302,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，产品目前在该工位不处于进站状态，不能上料。
        /// </summary>
        [Description("扫码上料请求异常，产品目前在该工位不处于进站状态，不能上料")]
        E1303 = 1303,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，没有找到对应产品信息。
        /// </summary>
        [Description("扫码上料请求异常，没有找到对应产品信息")]
        E1304 = 1304,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，产品在当前工位重复扫码。
        /// </summary>
        [Description("扫码上料请求异常，产品在当前工位重复扫码")]
        E1305 = 1305,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，上料步序已经上料。
        /// </summary>
        [Description("扫码上料请求异常，上料步序已经上料")]
        E1306 = 1306,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，物料条码规则不匹配。
        /// </summary>
        [Description("扫码上料请求异常，物料条码规则不匹配")]
        E1307 = 1307,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，关键物料有指定来源，但在指定工位没有过站数据。
        /// </summary>
        [Description("扫码上料请求异常，关键物料有指定来源，但在指定工位没有过站数据")]
        E1308 = 1308,

        /// <summary>
        /// 【关键物料扫码上料】扫码上料请求异常，关键物料有指定来源，但在指定工位过站NG。
        /// </summary>
        [Description("扫码上料请求异常，关键物料有指定来源，但在指定工位过站NG")]
        E1309 = 1309,

        /// <summary>
        /// 【批次料扫码上料】PLC 扫码请求数据不完整，Barcode 为空。
        /// </summary>
        [Description("PLC 扫码请求数据不完整，Barcode 为空")]
        E1321 = 1321,

        /// <summary>
        /// 【批次料扫码上料】没有找到对应的工位信息。
        /// </summary>
        [Description("没有找到对应的工位信息")]
        E1322 = 1322,

        /// <summary>
        /// 【批次料扫码上料】扫码上料请求异常，物料条码规则不匹配。
        /// </summary>
        [Description("扫码上料请求异常，物料条码规则不匹配")]
        E1323 = 1323,

        /// <summary>
        /// 【批次料扫码上料】扫码上料请求异常，没有找到 BOM 配置。
        /// </summary>
        [Description("扫码上料请求异常，没有找到 BOM 配置")]
        E1331 = 1331,

        /// <summary>
        /// 【批次料扫码上料】扫码上料请求异常，BOM 上料工序与当前工序不匹配。
        /// </summary>
        [Description("扫码上料请求异常，BOM 上料工序与当前工序不匹配")]
        E1332 = 1332,

        /// <summary>
        /// 【批次料扫码上料】扫码上料请求异常，当前产品没有找到可用的工单。
        /// </summary>
        [Description("扫码上料请求异常，当前产品没有找到可用的工单")]
        E1333 = 1333,

        /// <summary>
        /// 【批次料扫码上料】扫码上料请求异常，调用批次料接口异常。
        /// </summary>
        [Description("扫码上料请求异常，批次料接口调用异常")]
        E1334 = 1334,

        /// <summary>
        /// 【批次料扫码上料】扫码上料请求异常，调用批次料接口反馈异常。
        /// </summary>
        [Description("扫码上料请求异常，调用批次料接口反馈异常")]
        E1335 = 1335,

        #endregion

        #region 返工再上线

        /// <summary>
        /// 【返工再上线】返工再上线请求异常。
        /// </summary>
        [Description("返工再上线请求异常")]
        E1400 = 1400,

        /// <summary>
        /// 【返工再上线】PLC 返工再上线请求数据不完整，SN 为空。
        /// </summary>
        [Description("PLC 返工再上线请求数据不完整，SN 为空")]
        E1401 = 1401,

        /// <summary>
        /// 【返工再上线】返工再上线请求异常，返修件不存在。
        /// </summary>
        [Description("返工再上线请求异常，返修件不存在")]
        E1402 = 1402,

        /// <summary>
        /// 【返工再上线】返工再上线请求异常，产品不处于待重上线状态。
        /// </summary>
        [Description("返工再上线请求异常，产品不处于待重上线状态")]
        E1403 = 1403,

        /// <summary>
        /// 【返工再上线】返工再上线请求异常，产品还没有开返工单。
        /// </summary>
        [Description("返工再上线请求异常，产品还没有开返工单")]
        E1404 = 1404,

        /// <summary>
        /// 【返工再上线】返工再上线请求异常，产品还未完成返工作业。
        /// </summary>
        [Description("返工再上线请求异常，产品还未完成返工作业")]
        E1405 = 1405,

        #endregion

        #region 设备点检

        /// <summary>
        /// 【设备点检】设备点检异常。
        /// </summary>
        [Description("设备点检异常")]
        E1500 = 1500,

        /// <summary>
        /// 【设备点检】PLC 设备点检请求数据不完整，条码为空。
        /// </summary>
        [Description("PLC 设备点检请求数据不完整，条码为空")]
        E1501 = 1501,

        /// <summary>
        /// 【设备点检】PLC 设备点检请求异常，没有找到匹配的点检条码。
        /// </summary>
        [Description("PLC 设备点检请求异常，没有找到匹配的点检条码")]
        E1502 = 1502,

        #endregion

        #endregion

        #region 业务异常

        [Description("业务异常")]
        E4000 = 4000,

        /// <summary>
        /// 数据已存在
        /// </summary>
        [Description("数据已存在")]
        E4001 = 4001,

        /// <summary>
        /// 编号已存在
        /// </summary>
        [Description("编号已存在")]
        E4002 = 4002,

        /// <summary>
        /// 名称已存在
        /// </summary>
        [Description("名称已存在")]
        E4003 = 4003,

        /// <summary>
        /// 数据没找到
        /// </summary>
        [Description("数据没找到")]
        E4004 = 4004,

        /// <summary>
        /// 数据不唯一
        /// </summary>
        [Description("数据不唯一")]
        E4005 = 4005,

        /// <summary>
        /// 序号已存在
        /// </summary>
        [Description("序号已存在")]
        E4006 = 4006,

        /// <summary>
        /// 工序不唯一
        /// </summary>
        [Description("工序不唯一")]
        E4007 = 4007,

        /// <summary>
        /// 产品已和同一产线内其他工艺路线关联
        /// </summary>
        [Description("产品已和同一产线内其他工艺路线关联")]
        E4008 = 4008,

        /// <summary>
        /// 没有找到点检模板
        /// </summary>
        [Description("没有找到点检模板")]
        E4009 = 4009,

        /// <summary>
        /// 工单不能修改
        /// </summary>
        [Description("工单不能修改")]
        E4011 = 4011,

        /// <summary>
        /// 文件没找到
        /// </summary>
        [Description("文件没找到")]
        E4012 = 4012,

        /// <summary>
        /// 接口未实现
        /// </summary>
        [Description("接口未实现")]
        E4404 = 4404,

        #endregion
    }





    /// <summary>
    /// 数据处理结果
    /// </summary>
    /// <remarks>
    /// 返回客户端的错误代码还需参考 <see cref="ErrorCode"/>, 401~499 中间件内部错误。
    /// </remarks>
    public sealed class HandleResult
    {
        /// <summary>
        /// 状态，该值会回传给客户端。
        /// </summary>
        public int State { get; init; }

        /// <summary>
        /// 错误消息，null 表示没有。
        /// </summary>
        public string? ErrorMessage { get; init; }

        /// <summary>
        /// 内部错误代码。
        /// </summary>
        public int InnerErrorCode { get; set; }

        /// <summary>
        /// 要后续处理的数据。
        /// </summary>
        /// <remarks>若要对结果值进行额外处理，可进行转换获取，部分情况下不会设置值。</remarks>
        public object? Target { get; init; }

        /// <summary>
        /// 回写值
        /// </summary>
        public Dictionary<string, object>? CallbackItems { get; init; }

        /// <summary>
        /// 获取错误消息。优先取 <see cref="ErrorMessage"/> 值，若没有则取 <see cref="InnerErrorCode"/> 特性描述文本，若没有则返回 Empty。
        /// </summary>
        /// <returns></returns>
        public string GetErrorMessage()
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                return ErrorMessage;
            }

            if (Enum.IsDefined(typeof(ErrorCode), InnerErrorCode))
            {
                return ((ErrorCode)InnerErrorCode).Description();
            }

            return "";
        }

        /// <summary>
        /// 是否成功状态
        /// </summary>
        /// <returns></returns>
        public bool IsSuccess()
        {
            return State == (int)ErrorCode.E0010;
        }

        /// <summary>
        /// 返回OK结果（反馈 PLC 值为 E0010）
        /// </summary>
        /// <param name="callbackItems">回写值</param>
        /// <returns></returns>
        public static HandleResult Ok(Dictionary<string, object>? callbackItems = null)
        {
            return From((int)ErrorCode.E0010, 0, null, null, callbackItems);
        }

        /// <summary>
        /// 返回OK结果（反馈 PLC 值为 E0010）
        /// </summary>
        /// <param name="target">要后续处理的数据</param>
        /// <param name="callbackItems">回写值</param>
        /// <returns></returns>
        public static HandleResult Ok(object? target, Dictionary<string, object>? callbackItems = null)
        {
            return From((int)ErrorCode.E0010, 0, null, target, callbackItems);
        }

        /// <summary>
        /// 返回错误结果（反馈 PLC 值为 E0002）
        /// </summary>
        /// <param name="target"></param>
        /// <param name="callbackItems"></param>
        /// <returns></returns>
        public static HandleResult Error(object? target = null, Dictionary<string, object>? callbackItems = null)
        {
            return From((int)ErrorCode.E0002, (int)ErrorCode.E0002, null, target, callbackItems);
        }

        /// <summary>
        /// 返回错误结果
        /// </summary>
        /// <param name="err">Error 状态</param>
        /// <param name="target">要后续处理的数据</param>
        /// <param name="errorMessage">错误消息</param>
        /// <param name="callbackItems">回写值</param>
        /// <returns></returns>
        public static HandleResult Error(ErrorCode err, object? target = null, string? errorMessage = null, Dictionary<string, object>? callbackItems = null)
        {
            return From((int)err, (int)err, errorMessage, target, callbackItems);
        }

        /// <summary>
        /// 返回错误结果
        /// </summary>
        /// <param name="err">错误代码</param>
        /// <param name="innerErrCode">内部错误代码</param>
        /// <param name="errorMessage">错误消息</param>
        /// <param name="target">要后续处理的数据</param>
        /// <param name="callbackItems">回写值</param>
        /// <returns></returns>
        public static HandleResult Error(ErrorCode err, ErrorCode innerErrCode, string? errorMessage = null,
            object? target = null, Dictionary<string, object>? callbackItems = null)
        {
            return From((int)err, (int)innerErrCode, errorMessage, target, callbackItems);
        }

        /// <summary>
        /// 返回设置的数据
        /// </summary>
        /// <param name="state">状态</param>
        /// <param name="innerErrCode">内部错误代码</param>
        /// <param name="errorMessage">错误消息</param>
        /// <param name="target">要后续处理的数据</param>
        /// <param name="callbackItems">回写值</param>
        /// <returns></returns>
        public static HandleResult From(int state, int innerErrCode, string? errorMessage = null,
            object? target = null, Dictionary<string, object>? callbackItems = null)
        {
            return new()
            {
                State = state,
                InnerErrorCode = innerErrCode,
                ErrorMessage = errorMessage,
                Target = target,
                CallbackItems = callbackItems,
            };
        }
    }
}
